﻿package sandy.materials.attributes{	import sandy.core.Scene3D;
	import sandy.core.data.Matrix4;
	import sandy.core.data.Point3D;
	import sandy.core.data.Polygon;
	import sandy.core.scenegraph.Shape3D;
	import sandy.core.scenegraph.Sprite2D;
	import sandy.materials.Material;
	
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.geom.ColorTransform;
	import flash.utils.Dictionary;	
	/**	 * ABSTRACT CLASS - super class for all light attributes.	 *	 * <p>This class should not be directly instatiated, but sub classed.<br/>	 * The ALightAttributes class implements Blinn flavor of Phong reflection model.</p>	 *	 * @author		makc	 * @version		3.1	 * @date 		13.12.2007	 **/	public class ALightAttributes implements IAttributes	{		/**		 * Ambient reflection factor.		 *		 * <p>Note that since geometry of sprites is unknown, this is going to		 * be the only lighting setting affecting them, so you would typically		 * need to set it to bigger value than you would for shapes.</p>		 * @default 0.3		 */		public function get ambient ():Number		{			return _ambient;		}		/**		 * @private		 */		public function set ambient (p_nAmbient:Number):void		{			_ambient = p_nAmbient; onPropertyChange ();		}		/**		 * Diffuse reflection factor.		 * @default 1.0		 */		public function get diffuse ():Number		{			return _diffuse;		}		/**		 * @private		 */		public function set diffuse (p_nDiffuse:Number):void		{			_diffuse = p_nDiffuse; onPropertyChange ();		}		/**		 * Specular reflection factor.		 * @default 0.0		 */		public function get specular ():Number		{			return _specular;		}		/**		 * @private		 */		public function set specular (p_nSpecular:Number):void		{			_specular = p_nSpecular; onPropertyChange ();		}		/**		 * Specular exponent.		 * @default 5.0		 */		public function get gloss ():Number		{			return _gloss;		}		/**		 * @private		 */		public function set gloss (p_nGloss:Number):void		{			_gloss = p_nGloss; onPropertyChange ();		}		/**		 * Override this to respond to property changes.		 * @private		 */		protected function onPropertyChange ():void		{			;		}		/**		 * Latest light power.		 * @private		 */		protected var m_nI:Number;		/**		 * Latest light direction Point3D.		 * @private		 */		protected var m_oL:Point3D;		/**		 * Latest camera direction Point3D.		 * @private		 */		protected var m_oV:Point3D;		/**		 * Latest Blinn halfway Point3D between camera and light.		 * @private		 */		protected const m_oH:Point3D = new Point3D();		/**		 * Calculates the reflection for given normal.		 * @private		 */		protected function calculate (p_oNormal:Point3D, p_bFrontside:Boolean, p_bIgnoreSpecular:Boolean = false):Number		{			var l_n:Number = p_bFrontside ? -1 : 1;			var l_k:Number = l_n * m_oCurrentL.dot (p_oNormal); if (l_k < 0) l_k = 0; l_k = _ambient + _diffuse * l_k;			if (!p_bIgnoreSpecular && (specular > 0))			{				var l_s:Number = l_n * m_oCurrentH.dot (p_oNormal); if (l_s < 0) l_s = 0;				l_k += _specular * Math.pow (l_s, _gloss);			}			return l_k * m_nI;		}		/**		 * @private		 */		protected var m_oCurrentL:Point3D = new Point3D ();		/**		 * @private		 */		protected var m_oCurrentV:Point3D = new Point3D ();		/**		 * @private		 */		protected var m_oCurrentH:Point3D = new Point3D ();		/**		 * @private		 */		protected var m_oCurrentShape:Shape3D;		/**		* Draws light on shape.		*/		public function draw(p_oGraphics:Graphics, p_oPolygon:Polygon, p_oMaterial:Material, p_oScene:Scene3D):void		{			if (p_oMaterial.lightingEnable)			{				applyColorToDisplayObject (					p_oPolygon.shape.useSingleContainer ? p_oPolygon.shape.container : p_oPolygon.container,					p_oScene.light.color, 1				);				// compute local versions of Point3Ds				if (m_oCurrentShape != p_oPolygon.shape)				{					m_oCurrentShape = p_oPolygon.shape;					const invModelMatrix:Matrix4 = m_oCurrentShape.invModelMatrix;					if( m_oL )					{						m_oCurrentL.copy (m_oL);						invModelMatrix.transform3x3(m_oCurrentL);						m_oCurrentL.normalize ();					}					if( m_oV )					{						m_oCurrentV.copy (m_oV);						invModelMatrix.transform3x3 (m_oCurrentV);						m_oCurrentV.normalize ();					}					if( m_oH )					{						m_oCurrentH.copy (m_oH);						invModelMatrix.transform3x3 (m_oCurrentH);						m_oCurrentH.normalize ();					}				}			}		}		/**		* Draws light on sprite.		*/		public function drawOnSprite( p_oSprite:Sprite2D, p_oMaterial:Material, p_oScene:Scene3D ):void		{			if (p_oMaterial.lightingEnable)			{				applyColorToDisplayObject (p_oSprite.container, p_oScene.light.color,					ambient * p_oScene.light.getNormalizedPower ()				);			}		}		private function applyColorToDisplayObject (s:DisplayObject, c:uint, b:Number):void		{			// to avoid color darkening, we will normalize color; pitch-black is "normalized" to white			if ((c < 1) || (c > 0xFFFFFF))			{				c = 0xFFFFFF;			}			var rgb_r:Number = (0xFF0000 & c) >> 16;			var rgb_g:Number = (0x00FF00 & c) >> 8;			var rgb_b:Number = (0x0000FF & c);			const bY:Number = b * 1.7321 /*Math.sqrt (3)*/ / Math.sqrt (rgb_r * rgb_r + rgb_g * rgb_g + rgb_b * rgb_b);			rgb_r *= bY; rgb_g *= bY; rgb_b *= bY;			const ct:ColorTransform = s.transform.colorTransform;			if ((ct.redMultiplier != rgb_r) || (ct.greenMultiplier != rgb_g) || (ct.blueMultiplier != rgb_b))			{				ct.redMultiplier = rgb_r; ct.greenMultiplier = rgb_g; ct.blueMultiplier = rgb_b;				s.transform.colorTransform = ct;			}		}		/**		* @private		*/		public function begin( p_oScene:Scene3D ):void		{			// fetch light power			m_nI = p_oScene.light.getNormalizedPower ();			// fetch light direction Point3D			m_oL = p_oScene.light.getDirectionPoint3D ();			// fetch camera Point3D			m_oV = p_oScene.camera.getPosition ("absolute"); m_oV.scale (-1); m_oV.normalize ();			// compute Blinn halfway Point3D			m_oH.copy( m_oL ); m_oH.add (m_oV); m_oH.normalize ();			// clear current shape reference			m_oCurrentShape = null;			// init local Point3Ds to any valid values			m_oCurrentL.copy (m_oL); m_oCurrentV.copy (m_oV); m_oCurrentH.copy (m_oH);		}		/**		* @private		*/		public function finish( p_oScene:Scene3D ):void		{			;		}		/**		* @private		*/		public function init( p_oPolygon:Polygon ):void		{			;// to keep reference to the shapes/polygons that use this attribute		}		/**		* @private		*/		public function unlink( p_oPolygon:Polygon ):void		{			;// to remove reference to the shapes/polygons that use this attribute			if (m_oCurrentShape == p_oPolygon.shape)			{				m_oCurrentShape = null;			}		}		/**		* Flags for the attribute.		*/		public function get flags():uint		{			return m_nFlags;		}		/**		* @private		*/		protected var m_nFlags:uint = 0;//SandyFlags.INVERT_MODEL_MATRIX;		// --		private var _ambient:Number = 0.3;		private var _diffuse:Number = 1.0;		private var _specular:Number = 0.0;		private var _gloss:Number = 5.0;		private var _scenes:Dictionary = new Dictionary (true);	}}